3. Composer

A composer is responsible to initialize a component (or a component of tree) when ZK loader is composing a component. It is the controller in the MVC pattern, while the component is the view,which separate the code from the user interfaces.

3.1 Creating a composer

Composers can be created with the create-composer target. For example try running the following command from the root of a Grails project:
grails create-composer window

The command will result in the creation of a composer at the location grails-app/composers/WindowComposer.groovy:

class WindowComposer {
    def afterCompose = {Component comp ->
        // initialize components here
    }
}
WindowComposer by default provide a afterCompose closure to initialize components.

The create-composer command is merely for convenience and you can just as easily create composers using your favorite text editor or IDE

3.2 Apply a composer

Each component Taglib has a apply attribute,so you can place the composer here
<z:window id="myWindowComposer" apply="package.WindowComposer"></z:window>

In the WindowComposer side

def afterCompose = {Component comp ->
    assert comp.id=="myWindowComposer"
    assert (comp instanceof org.zkoss.zul.Window)
}

3.3 Auto-wired Component

If apply a composer to a Component that implement a IdSpace Interface,such as org.zkoss.zul.Window. The Component's children can be auto-wired to composer's fields

The following is an example. The onChange event received by Textbox mytextbox will be forwarded to target Window mywin as a new target event onChange_mytextbox and the Textbox component with id name "mytextbox" and Label with id name mylabel are injected into the "mytextbox" and "mylabel" fields respectively(so you can use mytextbox and mylabel variable directly in onChange_mytextbox without problem).

Composer

class MyComposer{
    Textbox mytextbox
    Window self //embeded object, the supervised window "mywin"
    Page page //the ZK page
    Label mylabel

def afterCompose = {Component comp -> assert mytextbox.id=="mytextbox" assert mylabel.id=="mylabel" }

def onChange_mytextbox(Event event) { mylabel.setValue("You just entered: "+ mytextbox.getValue()) } }

View

<z:window id="mywin" apply="MyComposer">
    <z:textbox id="mytextbox"/>
    <z:label id="mylabel"/>
</z:window>

3.4 Redirects

Actions can be redirected using the redirect method present in all composers

The parameters of redirect is same as controller's redirect

Composer

class MyComposer{
    Button mybutton

def afterCompose = {Component comp ->

}

def onClick_mybutton(Event event) { redirect(controller: 'demo', action: 'index', id: 1) } }

following code same as onClick_button above

def afterCompose = {Component comp ->
    mybutton.addEventListener('onClick'){
        redirect(controller: 'demo', action: 'index', id: 1)
    }
}

View

<z:window id="mywin" apply="MyComposer">
    <z:button label="mybutton"/>
</z:window>

3.5 Data Binding

To uses Grails' underlying data binding capability,zkui injection a getParams method to Component.

Domain Class

class Person {
    String firstName
    String lastName
    String fullName
    static constraints = {
    }
}

View

<z:window id="mywin" apply="MyComposer">
    <z:textbox name="firstName"/>
    <z:textbox name="lastName"/>
    <z:textbox name="fullName"/>
    <z:button label="submit"/>
</z:window>

Composer

class MyComposer{
    Button submit
    def afterCompose = {Component mywin ->
        submit.addEventListener('onClick'){
            def person=new Person(mywin.params)
            …
        }
    }
}

A bindData method same as in controller also provide to Composer

def p = new Person()
bindData(p, mywin.params)

3.6 Unit Test

When use grails create-composer create a composer,a unit class that is a sub-class of ComposerUnitTestCase also created

It provides a mockComposer methods for mocking zkui's Selector,Builder and so on.

mockComposer(MyComposer)
def myComposer=new MyComposer
...

3.7 Render Errors

zkui injection a renderErrors method to Component.

If you have Domain class

class Book{
    String author
    String title
}

In View

<z:window id="formWindow" title="demo" apply="your.Composer">
    <z:textbox name="author"/>
    <z:textbox name="title"/></z:window>

Then in your.Composer use renderErrors

if (!book.save()) {
            formWindow.renderErrors(bean: book)
 }

In addition,you can also use grails's traditional renderErrors

In your.Composer

if (!book.save()) {
    flash.book = book
    redirect(controller: "book", action: "edit", id: book.id)
 }

In your view

<g:hasErrors bean="${flash.book}">
<div class="errors">
    <g:renderErrors bean="${flash.book}" as="list" />
</div>
</g:hasErrors>

3.8 ZK's Data Binding

Data binding is a mechanism that automates the data-copy plumbing code (CRUD) between UI components and the data source. Application developers only have to tell the data binding manager about the associations between UI components and the data source. Then, the data -binding manager will do all the loading (loading data from the data source to UI components) and saving (saving data from UI component into the data source) jobs automatically.

Activates Data Binding Manager

<z:window  apply="org.zkoss.demo.MyComposer,org.zkoss.zkplus.databind.AnnotateDataBindingComposer">
</z:window>

For more information, please refer to the relative blog post Databinding Composer

Associate UI Components with Data Source

After activating the data-binding manager, you have to define the required UI objects and then associate them with the data source.

In the gsp view:

<z:window id="win" apply="test.TestComposer,org.zkoss.zkplus.databind.AnnotateDataBindingComposer">
    <z:textbox value="@{win#composer.username}"/>
<z:/window>

In the test.TestComposer:

class TestComposer{

def username="test"

def afterCompose = {Component comp -> … } }

See more ZK Developer's Reference/Data Binding